package com.ubi.core.log;

import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.spi.ILoggingEvent;
import com.ubi.core.framework.exception.RuntimeIOException;
import com.ubi.core.utils.ExceptionUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.MDC;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class TraceLogger {

    private static final TraceLogger INSTANCE = new TraceLogger();

    private final Map<Long, LoggingEventProcessor> processors = new ConcurrentHashMap<>();

    private PatternLayout layout;

    private String logFolder;

    private boolean shouldAlwaysFlushTraceLog = false;

    public static TraceLogger get() {
        return INSTANCE;
    }

    public void process(ILoggingEvent event) throws IOException {
        Long masterThreadId = getMasterThreadId();
        processors.get(masterThreadId).process(event);
    }

    public void clearUp(boolean flushTraceLog) {
        try {
            Long masterThreadId = getMasterThreadId();
            LoggingEventProcessor processor = processors.remove(masterThreadId);
            processor.clearUp(flushTraceLog);
        } catch (IOException e) {
            System.err.println("failed to clean up TraceLogger, exception=" + ExceptionUtils.stackTrace(e));
            throw new RuntimeIOException(e);
        }
    }

    public void clearAll() {
        processors.clear();
    }

    public void initialize() {
        MDC.clear();
        Long threadId = Thread.currentThread().getId();
        processors.put(threadId, new LoggingEventProcessor(layout, logFolder));
    }

    private Long getMasterThreadId() {
        String masterThreadId = MDC.get(LogConstants.MAIN_THREAD_ID);
        return NumberUtils.toLong(masterThreadId, Thread.currentThread().getId());
    }

    public void setLayout(PatternLayout layout) {
        this.layout = layout;
    }

    public void setLogFolder(String logFolder) {
        this.logFolder = logFolder;
    }

    public void enableAlwaysFlushTraceLog() {
        shouldAlwaysFlushTraceLog = true;
    }

    public void disableAlwaysFlushTraceLog() {
        shouldAlwaysFlushTraceLog = false;
    }

    public boolean shouldAlwaysFlushTraceLog() {
        return shouldAlwaysFlushTraceLog;
    }
}
